package com.bfly.mall.wechat.minipro.service;

import com.alibaba.fastjson.JSONObject;
import com.bfly.common.json.JaxbUtil;
import com.bfly.common.json.JsonUtil;
import com.bfly.common.util.DateUtil;
import com.bfly.common.util.StringUtil;
import com.bfly.core.base.entity.RemoteResponse;
import com.bfly.core.cache.SysConfigCacheUtil;
import com.bfly.core.context.IpThreadLocal;
import com.bfly.core.enums.SysError;
import com.bfly.core.exception.ServiceResponseException;
import com.bfly.mall.order.entity.Order;
import com.bfly.mall.order.entity.OrderSku;
import com.bfly.mall.system.dto.OrderConfigDTO;
import com.bfly.mall.system.dto.WXMiniProConfigDTO;
import com.bfly.mall.system.dto.WXPayConfigDTO;
import com.bfly.mall.wechat.common.WeChatAssert;
import com.bfly.mall.wechat.common.WeChatHttpUtil;
import com.bfly.mall.wechat.minipro.dto.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.*;

/**
 * 微信小程序订单API
 *
 * @author 长沙商脉科技有限公司
 * @date 2021/1/4 13:22
 */
public class WeChatMiniProOrderApi {

    private static Logger logger = LoggerFactory.getLogger(WeChatMiniProOrderApi.class);

    /**
     * 小程序应用向微信服务器提交预订单 返回交易号
     *
     * @param openId 支付者openId
     * @param order  订单信息
     * @author 长沙商脉科技有限公司
     * @date 2020/5/11 12:24
     */
    public static String payOrderToWeChat(String openId, Order order) {
        WXMiniProConfigDTO wxAppConfig = SysConfigCacheUtil.getWxMiniProConfig();
        WXPayConfigDTO wxPayConfig = SysConfigCacheUtil.getWxPayConfig();
        OrderConfigDTO orderConfig = SysConfigCacheUtil.getOrderConfig();

        WeChatOrder wxOrder = getWeChatOrder(order, wxAppConfig, wxPayConfig, orderConfig, openId);
        // 新提交的微信预订单
        JSONObject json = JsonUtil.toJsonFilter(wxOrder);
        RemoteResponse response;
        try {
            HttpPost httpPost = new HttpPost(WeChatMiniProAPi.ORDER_SUBMIT_JSAPI_V3);
            httpPost.setEntity(new StringEntity(json.toJSONString(), "UTF-8"));
            response = WeChatHttpUtil.weChatPayHttpRequestV3(httpPost);
        } catch (Exception e) {
            throw new ServiceResponseException(SysError.REMOTE_ERROR, "微信预下订单错误," + e.getMessage());
        }
        WeChatAssert.checkResponse(response);

        //预支付交易会话标识--- 该标识来判断是否需要重新提交微信预订单，在2小时效期内可用
        //再次提交微信预订单直接发起支付
        json = JsonUtil.parse(response.getBody(), JSONObject.class);
        String prepayId = json.getString("prepay_id");
        logger.info(String.format("订单提交微信成功:[%s,%s]", prepayId, wxOrder.getOut_trade_no()));
        return prepayId;
    }

    /**
     * 转换为微信识别的格式对象
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/5/11 13:51
     */
    private static WeChatOrder getWeChatOrder(Order order, WXMiniProConfigDTO wxMiniProConfig, WXPayConfigDTO wxPayConfig, OrderConfigDTO orderConfig, String openId) {
        if (StringUtils.isBlank(wxMiniProConfig.getAppId())) {
            throw new ServiceResponseException(SysError.MISSING_PARAM, "appId 为空!");
        }
        if (StringUtils.isBlank(wxPayConfig.getMchId())) {
            throw new ServiceResponseException(SysError.MISSING_PARAM, "mchId 为空!");
        }
        if (StringUtils.isBlank(wxPayConfig.getNotifyUrl())) {
            throw new ServiceResponseException(SysError.MISSING_PARAM, "支付回调地址为空!");
        }
        WeChatOrder wxOrder = new WeChatOrder();
        wxOrder.setAppid(wxMiniProConfig.getAppId());
        wxOrder.setMchid(wxPayConfig.getMchId());

        String description = "";
        List<OrderSku> items = order.getSkus();
        for (OrderSku item : items) {
            description = description.concat(item.getProductName()).concat(" " + item.getSkuName()).concat("×" + item.getBuyCount()).concat(",");
        }
        description = description.substring(0, description.length() - 1);
        if (description.getBytes().length > 127) {
            // 描述最多127字节长度
            description = new String(description.getBytes(), 0, 127);
        }
        wxOrder.setDescription(description);
        wxOrder.setOut_trade_no(order.getOrderNo());

        // 订单需在规定的分钟内支付
        Date tradeStartTime = new Date();
        Date tradeEndTime = DateUtils.addSeconds(tradeStartTime, orderConfig.getAutoCancelTime());
        wxOrder.setTime_expire(DateUtil.formatter(tradeEndTime, "yyyy-MM-dd'T'HH:mm:ssXXX"));

        wxOrder.setNotify_url(wxPayConfig.getNotifyUrl());


        // 订单金额
        WeChatOrder.Amount amount = wxOrder.new Amount();
        int totalFee = order.getPayPrice().multiply(new BigDecimal(100)).intValue();
        amount.setTotal(totalFee);
        amount.setCurrency("CNY");
        wxOrder.setAmount(amount);

        // 支付者
        WeChatOrder.Payer payer = wxOrder.new Payer();
        payer.setOpenid(openId);
        wxOrder.setPayer(payer);

        // 订单明细
        WeChatOrder.Detail detail = wxOrder.new Detail();
        List<WeChatOrder.Detail.GoodsDetail> goodsDetails = new ArrayList<>();

        for (OrderSku item : items) {
            WeChatOrder.Detail.GoodsDetail goodsDetail = detail.new GoodsDetail();
            goodsDetail.setGoods_name(item.getProductName() + " " + item.getSkuName());
            goodsDetail.setQuantity(item.getBuyCount());
            goodsDetail.setUnit_price(item.getSalePrice().multiply(new BigDecimal(100)).intValue());
            goodsDetail.setMerchant_goods_id(item.getSn());
            goodsDetails.add(goodsDetail);
        }
        detail.setGoods_detail(goodsDetails);
        detail.setCost_price(totalFee);
        wxOrder.setDetail(detail);

        // 支付场景信息
        WeChatOrder.sceneInfo sceneInfo = wxOrder.new sceneInfo();
        sceneInfo.setPayer_client_ip(IpThreadLocal.get());
        wxOrder.setScene_info(sceneInfo);
        return wxOrder;
    }

    /**
     * 从微信服务器查询订单交易信息
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/5/11 16:07
     */
    public static WeChatOrderQueryResponse queryOrderFromWeChat(String orderNo) {
        WXPayConfigDTO wxPayConfig = SysConfigCacheUtil.getWxPayConfig();
        RemoteResponse response;
        try {
            String url = WeChatMiniProAPi.ORDER_QUERY_V3.replace("{out_trade_no}", orderNo).concat("?mchid=" + wxPayConfig.getMchId());
            HttpGet httpGet = new HttpGet(url);
            response = WeChatHttpUtil.weChatPayHttpRequestV3(httpGet);
        } catch (Exception e) {
            throw new ServiceResponseException(SysError.REMOTE_ERROR, String.format("查询微信订单[%s]异常," + e.getMessage(), orderNo));
        }
        WeChatAssert.checkResponse(response);
        WeChatOrderQueryResponse queryOrderResponseMsg = JsonUtil.parse(response.getBody(), WeChatOrderQueryResponse.class);
        return queryOrderResponseMsg;
    }

    /**
     * 关闭订单
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/5/11 18:51
     */
    public static void closeOrderFromWeChat(String orderNo) {
        WXPayConfigDTO wxPayConfig = SysConfigCacheUtil.getWxPayConfig();
        try {
            String url = WeChatMiniProAPi.ORDER_CLOSE_V3.replace("{out_trade_no}", orderNo);

            JSONObject json = new JSONObject();
            json.put("mchid", wxPayConfig.getMchId());

            HttpPost httpPost = new HttpPost(url);
            StringEntity entity = new StringEntity(json.toJSONString());
            httpPost.setEntity(entity);

            RemoteResponse response = WeChatHttpUtil.weChatPayHttpRequestV3(httpPost);
            WeChatAssert.checkResponse(response);
            logger.info("关闭微信订单成功:" + orderNo);
        } catch (Exception e) {
            logger.error(String.format("关闭微信订单[%s]异常", orderNo), e);
            throw new ServiceResponseException(SysError.REMOTE_ERROR, "关闭微信订单异常," + e.getMessage());
        }
    }


    /**
     * 从微信服务器查询提交的订单信息
     *
     * @author 长沙商脉科技有限公司
     * @date 2021/1/4 13:21
     */
    public static WeChatOrderQueryResponse queryOrderTradeStatusFromWeChat(String orderNo) {
        WeChatOrderQueryResponse response = queryOrderFromWeChat(orderNo);
        return response;
    }

    /**
     * 发起微信退款操作
     *
     * @param orderNo 商户订单号
     * @author 长沙商脉科技有限公司
     * @date 2020/12/11 11:05
     */
    public static WeChatRefundOrderResponse refundOrderFromWeChat(String refundNo, String orderNo, String transactionId, BigDecimal refundFee, String reason) {
        WXMiniProConfigDTO wxMiniProConfig = SysConfigCacheUtil.getWxMiniProConfig();
        WXPayConfigDTO wxPayConfig = SysConfigCacheUtil.getWxPayConfig();
        int totalFee = refundFee.multiply(new BigDecimal(100)).intValue();
        String nonceStr = StringUtil.getRandomString(12);

        if (StringUtils.isNotEmpty(reason)) {
            if (reason.length() > 50) {
                reason = reason.substring(0, 50);
            }
        } else {
            reason = "无";
        }


        Map<String, String> param = new HashMap<>();
        param.put("appid", wxMiniProConfig.getAppId());
        param.put("mch_id", wxPayConfig.getMchId());
        param.put("nonce_str", nonceStr);
        param.put("out_refund_no", refundNo);
        param.put("out_trade_no", orderNo);
        param.put("total_fee", String.valueOf(totalFee));
        param.put("refund_fee", String.valueOf(totalFee));
        param.put("refund_desc", reason);
        param.put("transaction_id", transactionId);
        String sign = WeChatMiniProAPi.getWeChatPayV1Sign(param, wxPayConfig.getApiKeyV1());

        WeChatRefundOrder refundOrder = new WeChatRefundOrder();
        refundOrder.setTransactionId(transactionId);
        refundOrder.setOutTradeNo(orderNo);
        refundOrder.setOutRefundNo(refundNo);
        refundOrder.setTotalFee(totalFee);
        refundOrder.setRefundFee(totalFee);
        refundOrder.setAppId(wxMiniProConfig.getAppId());
        refundOrder.setMchId(wxPayConfig.getMchId());
        refundOrder.setNonceStr(nonceStr);
        refundOrder.setRefundDesc(reason);
        refundOrder.setSign(sign);

        String xml = JaxbUtil.toXml(refundOrder);
        HttpPost post = new HttpPost(WeChatMiniProAPi.ORDER_REFUND_V1);
        StringEntity entity = new StringEntity(xml, "UTF-8");
        post.setEntity(entity);
        RemoteResponse response = WeChatHttpUtil.weChatPayHttpsRequestV1(post);
        WeChatAssert.checkResponse(response);

        String body = response.getBody();
        WeChatRefundOrderResponse refundOrderResponse = JsonUtil.parse(body, WeChatRefundOrderResponse.class);
        logger.info(String.format("提交微信退款申请成功:[%s,%s]", refundNo, orderNo));
        return refundOrderResponse;
    }

    /**
     * 查询退款订单退款状态
     *
     * @param refundId 第三方支付平台退款单号
     * @author 长沙商脉科技有限公司
     * @date 2020/12/11 14:02
     */
    public static WeChatRefundOrderQueryResponse queryRefundOrderStatusFromWeChat(String refundId) {
        try {
            WXMiniProConfigDTO wxAppConfig = SysConfigCacheUtil.getWxMiniProConfig();
            WXPayConfigDTO wxPayConfig = SysConfigCacheUtil.getWxPayConfig();
            String nonceStr = StringUtil.getRandomString(12);

            Map<String, String> param = new HashMap<>();
            param.put("appid", wxAppConfig.getAppId());
            param.put("mch_id", wxPayConfig.getMchId());
            param.put("nonce_str", nonceStr);
            param.put("refund_id", refundId);
            String sign = WeChatMiniProAPi.getWeChatPayV1Sign(param, wxPayConfig.getApiKeyV1());

            WeChatRefundOrderQuery query = new WeChatRefundOrderQuery();
            query.setRefundId(refundId);
            query.setAppId(wxAppConfig.getAppId());
            query.setMchId(wxPayConfig.getMchId());
            query.setNonceStr(nonceStr);
            query.setSign(sign);

            String xml = JaxbUtil.toXml(query);
            HttpPost post = new HttpPost(WeChatMiniProAPi.ORDER_REFUND_QUERY_V1);
            StringEntity entity = new StringEntity(xml);
            post.setEntity(entity);
            RemoteResponse response = WeChatHttpUtil.weChatPayHttpsRequestV1(post);
            WeChatAssert.checkResponse(response);

            xml = response.getBody();
            JSONObject json = JsonUtil.parse(xml, JSONObject.class);
            WeChatRefundOrderQueryResponse queryResponse = new WeChatRefundOrderQueryResponse();
            json.forEach((key, data) -> {
                if (key.contains("refund_status")) {
                    queryResponse.setRefundStatus(String.valueOf(data));
                } else if (key.contains("refund_success_time")) {
                    queryResponse.setRefundSuccessTime(DateUtil.parseStrDateTime(String.valueOf(data)));
                }
            });
            return queryResponse;
        } catch (Exception e) {
            throw new ServiceResponseException(SysError.ERROR, e.getMessage());
        }
    }
}